package com.hedl.practice.diyJava.encryptionandsecurity.asymmetric_encryption_algorithm;

import com.hedl.practice.diyJava.encryptionandsecurity.asymmetric_encryption_algorithm.tmp.Person;

import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.NoSuchAlgorithmException;

/**
 * 在实际应用的时候，非对称加密总是和对称加密一起使用。假设小明需要给小红需要传输加密文件，他俩首先交换了各自的公钥，然后：
 *      小明生成一个随机的AES口令，然后用小红的公钥通过RSA加密这个口令，并发给小红；
 *      小红用自己的RSA私钥解密得到AES口令；
 *      双方使用这个共享的AES口令用AES加密通信。
 *
 * 可见非对称加密实际上应用在第一步，即加密“AES口令”。
 * 这也是我们在浏览器中常用的HTTPS协议的做法，即浏览器和服务器先通过RSA交换AES口令，
 * 接下来双方通信实际上采用的是速度较快的AES对称加密，而不是缓慢的RSA非对称加密。
 *
 * 以RSA算法为例，它的密钥有256/512/1024/2048/4096等不同的长度。长度越长，密码强度越大，当然计算速度也越慢。
 * 如果修改待加密的byte[]数据的大小，可以发现，使用512bit的RSA加密时，
 * 明文长度不能超过53字节，使用1024bit的RSA加密时，明文长度不能超过117字节，这也是为什么使用RSA的时候，
 * 总是配合AES一起使用，即用AES加密任意长度的明文，用RSA加密AES口令。
 *
 * 小结：
 *      非对称加密就是加密和解密使用的不是相同的密钥，只有同一个公钥-私钥对才能正常加解密；
 *      只使用非对称加密算法不能防止中间人攻击。
 */
public class Client {
    //创建公钥/私钥对
    static Person alice;

    static {
        try {
            alice = new Person("Alice");
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        }
    }

    public static void main(String[] args) {
        //明文
        byte[] plain = "Hello,encrypt use RSA".getBytes(StandardCharsets.UTF_8);

        //用Alice的公钥加密
        byte[] pk = alice.getPublicKey();
        System.out.println(String.format("public key: %x",new BigInteger(1,pk)));
        byte[] encrypted = alice.encrypt(plain);
        System.out.println(String.format("encrypted: %x",new BigInteger(1,encrypted)));

        //用Alice的私钥解密
        byte[] sk = alice.getPrivateKey();
        System.out.println(String.format("private key: %x",new BigInteger(1,sk)));
        byte[] decrypted = alice.decrypt(encrypted);
        System.out.println(new String(decrypted,StandardCharsets.UTF_8));
    }

    /**
     * 要从byte[]数组恢复公钥或私钥，可以这么写：
     *      byte[] pkData = ...
     *      byte[] skData = ...
     *      KeyFactory kf = KeyFactory.getInstance("RSA");
     *      // 恢复公钥:
     *      X509EncodedKeySpec pkSpec = new X509EncodedKeySpec(pkData);
     *      PublicKey pk = kf.generatePublic(pkSpec);
     *      // 恢复私钥:
     *      PKCS8EncodedKeySpec skSpec = new PKCS8EncodedKeySpec(skData);
     *      PrivateKey sk = kf.generatePrivate(skSpec);
     */
}
